#include "precompiled.h"
#include "sslbase.h"
#include "common.h"
#include <protocol/message.h>

namespace Opl
{
	namespace Helpers
	{
		namespace Ssl
		{

			using Opl::Core::EntityObject;
			using namespace Opl::Protocol;

			class SslBasePrivate
			{
				public:
					SslBasePrivate(): status(Disconnected), socket(0)
					{}
					~SslBasePrivate() {}
					Opl::Helpers::ConnectionStatus status;
					QSslSocket* socket;
					Opl::Helpers::ConnectionStatus updateState()const {
						if (status == Connecting) {
							return status;
						}

						bool unconnected = !socket || socket->state() == QAbstractSocket::UnconnectedState;

						if (unconnected) {
							return Disconnected;
						}

						bool connected = socket && socket->state() == QAbstractSocket::ConnectedState;

						if (connected) {
							return Connected;
						}

						return status;
					}
			};


			SslBase::SslBase(QSslSocket* socket) :
				Opl::Core::EntityObject(0), O_PRIVATE_CREATE(SslBase)
			{
				O_D(SslBase);
				d->socket = socket;
				logger()->debug("Create SslBase");
			}

			SslBase::~SslBase()
			{
				O_PRIVATE_DELETE;
			}

			void SslBase::setSocket(QSslSocket* s)
			{
				O_D(SslBase);
				Q_ASSERT(s);
				d->socket = s;
			}

			QSslSocket* SslBase::socket()const
			{
				O_C(SslBase);
				return d->socket;
			}

			void SslBase::setStatusInternal(Opl::Helpers::ConnectionStatus s)
			{
				O_D(SslBase);
				d->status = s;
			}

			Opl::Helpers::ConnectionStatus SslBase::status()const
			{
				O_C(SslBase);
				return d->updateState();
			}

			void SslBase::sslErrors(const QList<QSslError>& errors)
			{
				O_D(SslBase);
				foreach(QSslError er, errors) {
					logger()->error("SslError:%1", er.errorString());
				}
				d->socket->ignoreSslErrors();
			}

			void SslBase::modeChanged(QSslSocket::SslMode mode)
			{
				logger()->info("Ssl modeChanged:%1", mode);
			}

			void SslBase::peerVerifyError(const QSslError& error)
			{
				logger()->error("SslError-peerVerifyError:%1", error.errorString());
			}

			void SslBase::socketConnected()
			{
				logger()->info("socketConnected");
			}

			void SslBase::socketDisconnected()
			{
				O_D(SslBase);
				logger()->info("socketDisconnected");
				d->status = Disconnected;
				d->updateState();
				emit disconnected();
			}

			void SslBase::error(QAbstractSocket::SocketError socketError)
			{
				logger()->info("SocketError:%1", socketError);
			}

			void SslBase::hostFound()
			{
				logger()->info("hostFound");
			}

			void SslBase::proxyAuthenticationRequired (const QNetworkProxy& proxy, QAuthenticator* authenticator)
			{
				logger()->info("proxyAuthenticationRequired:%1", proxy.hostName());
			}

			void SslBase::stateChanged(QAbstractSocket::SocketState socketState)
			{
				logger()->info("SocketState:%1", socketState);
				O_D(SslBase);
				d->updateState();

				if (socketState == QAbstractSocket::UnconnectedState) {
					logger()->info("UnconnectedState");
					d->socket->deleteLater();
					d->socket = 0;
				}
			}

			void SslBase::ready()
			{
				O_D(SslBase);

				if (!d->socket) {
					logger()->error("encrypted ready comand but socket is null (can be already disconnected)");
					return;
				}

				QSslCipher ciph = d->socket->sessionCipher();
				QString cipher = QString("%1, %2 (%3/%4)").arg(ciph.authenticationMethod()).arg(ciph.name()).arg(ciph.usedBits()).arg(ciph.supportedBits());
				logger()->info("SslBase::Reconnect:ready() cipher:%1", cipher);
				d->status = Connected;
				emit connected();
			}

			void SslBase::readyCommand()
			{
				O_D(SslBase);
				QSslSocket *client = (QSslSocket*)sender();
				QByteArray arr = client->readAll();

				QDataStream sstream(arr);
				Message msg;
				sstream >> msg;
			
				emit output(arr);
			}

			void SslBase::Send(const QString& data)
			{
				O_D(SslBase);
				QDataStream datastr(d->socket);
				Message msg;//TODO: = Message::createCommand(data);
				datastr << msg;
				d->socket->flush();
			}

			void SslBase::stop()
			{
				O_D(SslBase);
				d->socket->close();
				delete d->socket;
				d->socket = 0;
				d->updateState();
			}

			void SslBase::start()
			{
				O_D(SslBase);
				Q_ASSERT(d->socket);
				logger()->info("SslBase::start");
				connect(d->socket, SIGNAL(stateChanged(QAbstractSocket::SocketState)), this, SLOT(stateChanged(QAbstractSocket::SocketState)));
				connect(d->socket, SIGNAL(encrypted()), this, SLOT(ready()));
				connect(d->socket, SIGNAL(sslErrors(QList<QSslError>)), this, SLOT(sslErrors(QList<QSslError>)));
				connect(d->socket, SIGNAL(readyRead()), this, SLOT(readyCommand()));
				connect(d->socket, SIGNAL(hostFound()), this, SLOT(hostFound()));
				connect(d->socket, SIGNAL(peerVerifyError(const QSslError&)), this, SLOT(peerVerifyError(const QSslError&)));
				connect(d->socket, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(error(QAbstractSocket::SocketError)));
				connect(d->socket, SIGNAL(connected()), this, SLOT(socketConnected()));
				connect(d->socket, SIGNAL(disconnected()), this, SLOT(socketDisconnected()));
				connect(d->socket, SIGNAL(modeChanged(QSslSocket::SslMode)), this, SLOT(modeChanged(QSslSocket::SslMode)));
				connect(d->socket, SIGNAL(proxyAuthenticationRequired(const QNetworkProxy&, QAuthenticator*)), this, SLOT(proxyAuthenticationRequired(const QNetworkProxy&, QAuthenticator*)));
				foreach(QSslCipher c, d->socket->defaultCiphers()) {
					logger()->trace("Ciphers:%1", c.name());
				}
				d->updateState();
			}

		}
	}
}